#include <stdbool.h>

#include <SDL2/SDL_mixer.h>

#include "wave.h"
#include "kill.h"
#include "exit_status.h"

static int is_stereo;

void update_wave_8(void *udata, Uint8 *stream, int len) {
	if (is_stereo) {
		wave_shift(0, len / 2);
		wave_shift(1, len / 2);
		for (int i = 0; i < len; i += 2) {
			wave_push_8(stream[i], 0);
			wave_push_8(stream[i + 1], 1);
		}
	}
	else {
		for (int i = 0; i < len; ++i) {
			wave_push_8(stream[i], 0);
		}
	}
}

void update_wave_16(void *udata, Uint8 *stream_8, int len) {
	uint16_t *stream = (uint16_t*)(stream_8);
	len /= 2;
	if (is_stereo) {
		wave_shift(0, len / 2);
		wave_shift(1, len / 2);
		for (int i = 0; i < len; i += 2) {
			wave_push_16(stream[i], 0);
			wave_push_16(stream[i + 1], 1);
		}
	}
	else {
		for (int i = 0; i < len; ++i) {
			wave_push_16(stream[i], 0);
		}
	}
}

void update_wave_32(void *udata, Uint8 *stream_8, int len) {
	uint32_t *stream = (uint32_t*)(stream_8);
	len /= 4;
	if (is_stereo) {
		wave_shift(0, len / 2);
		wave_shift(1, len / 2);
		for (int i = 0; i < len; i += 2) {
			wave_push_32(stream[i], 0);
			wave_push_32(stream[i + 1], 1);
		}
	}
	else {
		for (int i = 0; i < len; ++i) {
			wave_push_32(stream[i], 0);
		}
	}
}


static Mix_Music *music = NULL;

void cleanup_music(void) {
	if (music) {
		Mix_PauseMusic();
		Mix_SetPostMix(NULL, NULL);
		Mix_FreeMusic(music);
		music = NULL;
	}
}

void init_music() {
	static int has_initialized = false;
	if (has_initialized) return;
	has_initialized = true;

	if (Mix_Init(0xFFFFFFFF)) {
		kill_sdl("Failed to initialize SDL mixer", EX_ERR_MIX_INIT);
	}

	if (Mix_OpenAudio(2 * 44100, MIX_DEFAULT_FORMAT, 2, 512) < 0) {
		kill_sdl("Failed to open audio device", EX_ERR_MIX_OPEN_AUDIO);
	}

	kill_push(cleanup_music);
}

int music_playing() {
	return Mix_PlayingMusic();
}

void toggle_music() {
	if (Mix_PausedMusic()) {
		Mix_ResumeMusic();
	}
	else {
		Mix_PauseMusic();
	}
}

int music_paused() {
	return Mix_PausedMusic();
}

void play_music(const char *path) {
	init_music();

	if (music) {
		Mix_FreeMusic(music);
		music = NULL;
	}

	music = Mix_LoadMUS(path);
	if (music) {
		int channels;
		uint16_t format;
		Mix_QuerySpec(NULL, &format, &channels);
		if (channels == 1) is_stereo = 0;
		else if (channels == 2) is_stereo = 1;
		switch (format & 0xFF) {
		case 8:
			Mix_SetPostMix(update_wave_8, NULL);
			break;
		case 16:
			Mix_SetPostMix(update_wave_16, NULL);
			break;
		case 32:
			Mix_SetPostMix(update_wave_32, NULL);
			break;
		}
		Mix_PlayMusic(music, 0);
	}
}